package com.atguigu.gulimall.order.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * @author Yanglinfengkk
 */
@Configuration
public class MyRabbitMqConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 使用JSON序列化机制,进行消息转换
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     *
     * @PostConstruct MyRabbitMqConfig对象创建完成以后, 构造执行完执行这个方法
     * 1.服务收到消息就回调
     *      1.spring.rabbitmq.publisher-confirms=true
     *      2.设置确认回调    ConfirmCallback
     * 2.消息正确抵达队列进行回调
     *      1.spring.rabbitmq.publisher-returns=true
     *      spring.rabbitmq.template.mandatory=true
     *      2.设置确认回调ReturnCallback
     *
     * 3.消费端(保证每个消息被正确消费,此时才可以Broker删除这个消息)
     *       spring.rabbitmq.listener.simple.acknowledge-mode=manual
     *      1.默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个信息
     *          问题:
     *              我们收到很多消息,自动回复给服务器ack,只有一个处理成功,然后宕机了,发生了消息丢失
     *              消费者手动确认默认,只要我们没有明确告诉MQ,没有被签收,相当于没有ack,消息一直是Unacked(未确认)状态
     *                  即使Consumer(消费者)宕机,消息不会丢失,会重新变为Ready(准备)状态,下一次有新的Consumer(客户端)连接进来就发给他进行处理
     *
     *      2.如何签收信息
     *          long deliveryTag = message.getMessageProperties().getDeliveryTag();
     *          签收消息/非批量模式  deliveryTag(处理数量) multiple(是否批量操作)
     *          channel.basicAck(deliveryTag,false) 签收消息
     *
     *          拒绝签收  requeue=false 丢弃    true   发挥服务器/服务器重新入队
     *          Long deliveryTag(处理数量),boolean multiple(是否批量操作),boolean requeue(重新入队)
     *          channel.basicNack(deliveryTag,false,true);
     *          Long deliveryTag(处理数量),boolean requeue(重新入队)
     *          channel.basicReject(deliveryTag,true);
     *
     */
    @PostConstruct
    public void initRabbitTemplate() {
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

            /**
             *  1.只要消息抵达Broker(代理)ack=true
             * @param correlationData   当前消息的唯一关联数据(唯一ID)
             * @param b     消息是否成功收到
             * @param s     失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("confirm...correlationData[" + correlationData + "]" +
                        "===>[" + b + "]===>[" + s + "]");
            }
        });
        //设置消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递给指定的队列,就出发这个失败回调
             * @param message       投递失败消息详细信息
             * @param i             回复状态码
             * @param s             回复的文本内容
             * @param s1            当时这个消息发送给那个交换机
             * @param s2            当时这个消息用那个路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("Fail Message["+message+"]==>replyCode["+i+"]" +
                        "replyTest["+s+"]\"==>exchange["+s1+"]==>routingKey["+s2+"]");
            }
        });
    }
}
